<script>
/*
 * @Author: 焦质晔
 * @Date: 2020-02-02 10:26:05
 * @Last Modified by: 焦质晔
 * @Last Modified time: 2020-03-11 12:09:51
 */
import _ from 'lodash';
import XLSX from 'xlsx';
import { notifyAction } from '@/utils';
import { download } from './download.js';
import { Container } from 'element-ui';

export default {
  name: 'JsonToExcel',
  props: {
    // Json to download
    initialValue: {
      type: Array,
      required: true,
      default: () => []
    },
    // fields inside the Json Object that you want to export
    fields: {
      type: Object,
      required: true,
      default: () => ({})
    },
    // Use as fallback when the row has no field values
    defaultValue: {
      type: String,
      default: ''
    },
    // mime type [xlsx, csv]
    fileType: {
      type: String,
      default: 'xlsx'
    },
    // filename to export
    fileName: {
      type: String
    },
    // ajax function
    fetch: {
      type: Object,
      default: () => ({})
    },
    // sheet prefix
    workSheet: {
      type: String,
      default: 'sheet'
    },
    // format data handle
    formatHandle: {
      type: Function
    },
    // event before generate was called
    beforeGenerate: {
      type: Function
    },
    // event before download pops up
    beforeFinish: {
      type: Function
    }
  },
  data() {
    this.workbook = {
      SheetNames: [],
      Sheets: {}
    };
    return {
      loading: false
    };
  },
  computed: {
    // unique identifier
    idName() {
      var now = new Date().getTime();
      return 'export_' + now;
    },
    wbopts() {
      return {
        bookType: this.fileType,
        bookSST: false, // 是否生成Shared String Table，官方解释是，如果开启生成速度会下降，但在低版本IOS设备上有更好的兼容性
        type: 'binary'
      };
    }
  },
  methods: {
    async generate() {
      if (_.isFunction(this.beforeGenerate)) {
        await this.beforeGenerate();
      }
      let { api, params, datakey } = this.fetch;
      let data = this.initialValue;
      if (api) {
        try {
          this.loading = !0;
          data = [];
          let current = 0;
          const pageSize = 500;
          while ((current * pageSize) < params.limit) {
            current ++;
            const res = await api({
              ...params,
              currentPage: current,
              pageSize, // 必须
              pageNum: current,
              limit: pageSize,
              current, // 必须
              size: pageSize
            });

            if (res && res.resultCode === 200) {
            let resData = (!datakey ? res.data : _.get(res.data, datakey)) || [];
            data.push(...resData);
            }
          }
        } catch (err) {}
        this.loading = !1;
      }
      if (!data.length) {
        return notifyAction('数据为空，无法导出！', 'warning');
      }
      if (this.formatHandle) {
        data = this.formatHandle(data);
      }
      let json = this.getProcessedJson(data, this.fields);
      // 执行导出
      this.export(this.getSheetData([json]), this.fileName);
    },
    getSheetData(data) {
      this.clearWorkbook();
      data.forEach((el, index) => {
        const colStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        const sheetName = `${this.workSheet}${index + 1}`;
        let sheet = XLSX.utils.json_to_sheet(el);
        this.workbook.SheetNames.push(sheetName);
        el.map((x, n) => {
          // x:  {序号: 1, 建立时间: "2020-06-18 15:50:56", 试驾开始时间: "2020-06-18 15:50:25", 试驾结束时间: "2020-06-18 16:05:01", 试驾车辆: "沪CU252B", …}
          let i = 0; // 统计表头列数
          for (let k in x) {
            const CSF = JSON.parse(this.fields[k].split('|')[1]); // this.fields[k] ===> this.fields[序号]: "index|{}"
            let key = '';
            if (i < 26) {
              key = colStr.slice(i, ++i) + (n + 2); // 获取 Excel 行坐标： A2、B2、C2、D2 (排除表头)
            } else if (i < 676) {
              // 获取 Excel 行坐标： AA2、AB2、AC2、AD2 (排除表头)
              key = colStr.slice(Math.floor(i / 26) - 1, Math.floor(i / 26)) + colStr.slice(i % 26, (i % 26) + 1) + (n + 2); // 修复26列以后key的取值问题
              ++i;
            }
            sheet[key] && sheet[key].v === '' && (CSF.t = 's'); // 值为空的情况下将类型设置为字符防止格式化后出现NAN
            Object.assign(sheet[key] || {}, CSF);
          }
        });
        this.workbook.Sheets[sheetName] = sheet;
      });
      return XLSX.write(this.workbook, this.wbopts);
    },
    async export(data, filename) {
      let blob = this.sheetToBlob(data);
      if (_.isFunction(this.beforeFinish)) {
        await this.beforeFinish();
      }
      const res = download(blob, filename);
      res ? this.$emit('onFinish') : this.$emit('onError');
    },
    getProcessedJson(data, header) {
      let keys = this.getKeys(data, header);
      let newData = [];
      data.forEach((item, index) => {
        let newItem = {};
        for (let label in keys) {
          let property = keys[label];
          newItem[label] = this.getValue(property, item);
        }
        newData.push(newItem);
      });
      return newData;
    },
    getKeys(data, header) {
      let keys = {};
      if (header) {
        for (let k in header) {
          // header[k] ===> "index|{}"
          keys[k] = header[k].split('|')[0];
        }
        return keys;
      }
      for (let key in data[0]) {
        keys[key] = key;
      }
      return keys;
    },
    getValue(key, item) {
      const field = _.isObject(key) ? key.field : key;
      let indexes = Array.isArray(field) ? field : field.split('.');
      let value = this.defaultValue;
      if (!field) value = item;
      else if (indexes.length > 1) value = this.getValueFromNestedItem(item, indexes);
      else value = this.parseValue(item[field]);
      if (key.hasOwnProperty('callback')) value = this.getValueFromCallback(value, key.callback);
      return value;
    },
    getValueFromNestedItem(item, indexes) {
      let nestedItem = item;
      for (let index of indexes) {
        if (nestedItem) nestedItem = nestedItem[index];
      }
      return this.parseValue(nestedItem);
    },
    getValueFromCallback(item, callback) {
      if (!_.isFunction(callback)) return this.defaultValue;
      const value = callback(item);
      return this.parseValue(value);
    },
    parseValue(value) {
      return value || value === 0 || _.isBoolean(value) ? value : this.defaultValue;
    },
    clearWorkbook() {
      this.workbook.SheetNames = [];
      this.workbook.Sheets = {};
    },
    sheetToBlob(data) {
      function s2ab(s) {
        var buf = new ArrayBuffer(s.length);
        var view = new Uint8Array(buf);
        for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
        return buf;
      }
      return new Blob([s2ab(data)], { type: 'application/octet-stream' }); // 字符串转 ArrayBuffer
    }
  },
  render() {
    const { $props, $listeners, $attrs, $slots, loading } = this;
    const wrapProps = {
      key: this.idName,
      props: {
        ...$props,
        loading
      },
      attrs: {
        id: this.idName,
        ...$attrs,
        icon: 'el-icon-download'
      },
      on: {
        click: this.generate
      }
    };
    return (
      <div>
        <el-button {...wrapProps}>{$slots['default']}</el-button>
      </div>
    );
  }
};
</script>

<style lang="less" scoped></style>
